package namespace

import (
	"errors"
	"fmt"
	"reflect"

	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/binocle/module/report"
	. "gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/achievement"
	. "gddgame.cc/galaxy/common/dsl/game/ad"
	"gddgame.cc/galaxy/common/dsl/game/reward"
	. "gddgame.cc/galaxy/common/dsl/game/user"
	. "gddgame.cc/galaxy/common/dsl/web"
	"gddgame.cc/galaxy/core/sdk"
	"gddgame.cc/galaxy/project/maze/maze/controller"
	"gddgame.cc/galaxy/project/maze/maze/model"
	"gddgame.cc/galaxy/project/maze/maze/model/player_single"
	"gddgame.cc/galaxy/satellite/formula/nebula"
	"gddgame.cc/galaxy/server/composer"
	"gddgame.cc/galaxy/utils"
)

const (
	InfoSelf nebula.NSE = iota
	InfoUser
	InfoAchievement
	InfoModel
	InfoBuyModel
	InfoUseModel
	InfoSingle
	InfoFinishSingle
	InfoStat
	InfoFriend
	InfoFriendInvited
	InfoSearch
	InfoRank
	InfoRequestAd
	InfoResponseAd
	InfoReceiveReward
	InfoGameResult
)

/*
 游戏信息加载
*/
func InfoHandler(builder composer.Builder, client *sdk.AppClient, namespace nebula.Namespace) {
	var agent binocle.App
	builder.Factory("agent", &agent)

	namespace.SetCompress(false)

	reporter := agent.AddMetrics().Period(report.Minute1).Type(report.BothMetric).
		Aggregate(report.Sum, report.Max).Reporter()

	if err := namespace.Connect(func(socket *nebula.Socket, next nebula.Next) {
		var openId string
		err := socket.Get("open_id", &openId)
		if err != nil {
			_ = next(errors.New("Auth error"))
			return
		}
		_ = next(nil)

	}); err != nil {
		builder.Error(err)
	}

	_ = namespace.On(InfoSelf, func(req *nebula.Request) {
		var openId string
		var user model.User
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		_ = controller.Env.RefreshUserStatus(openId, false)
		language := GetLanguage(req)
		detail, err := controller.Center.GetUserDetail(openId, &user, language)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		userDetail, err := controller.GetUserDetailInfo(req, &user, language)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		userDetail.BindGameUser(detail)

		req.SetCompress(true)
		req.Response(detail)
	})

	_ = namespace.On(InfoUser, func(req *nebula.Request, targetId string) {
		var openId string
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		var user model.User
		language := GetLanguage(req)
		info, err := controller.Center.GetUserInfo(openId, targetId, &user, language)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		req.SetCompress(true)
		req.Response(info)
	})

	_ = namespace.On(InfoAchievement, func(req *nebula.Request, updateTime int) {
		var items []*Achievement
		var err error
		//language := GetLanguage(req)
		items, err = controller.Center.GetAllAchievement(GetUpdateTime(req, updateTime))
		if err != nil {
			req.ResponseErr(err)
			return
		}
		req.SetCompress(true)
		req.Response(items)
	})

	_ = namespace.On(InfoModel, func(req *nebula.Request, updateTime int) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			return
		}
		models, err := controller.Center.GetAllModel(userId, user.Entity, GetUpdateTime(req, updateTime))
		if err != nil {
			req.ResponseErr(err)
			return
		}
		req.SetCompress(true)
		req.Response(models)
	})

	_ = namespace.On(InfoBuyModel, func(req *nebula.Request, modelKey string) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		if controller.Center.HasModel(userId, modelKey) {
			req.SendError(errors.New("MODEL_HAS"))
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		updateData := make(map[string]interface{})
		if controller.Center.CanModel(modelKey, user.Gold) {
			if err := controller.Center.AddModel(userId, modelKey, updateData); err != nil {
				return
			}
			if err := controller.Center.UpdateUser(&user, updateData); err != nil {
				req.SendError(err)
				req.Response(false)
				return
			}
			req.Response(true)
			return
		} else {
			req.SendError(ValidException("GOLD_NOENOUGH"))
			req.Response(false)
		}

	})

	_ = namespace.On(InfoUseModel, func(req *nebula.Request, modelKey string) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		if !controller.Center.HasModel(userId, modelKey) {
			req.SendError(ValidException("MODEL_NO_HAS"))
			req.Response(false)
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		if err := controller.Center.UpdateUser(&user, map[string]interface{}{
			"entity": modelKey,
		}); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		req.Response(true)
	})

	_ = namespace.On(InfoSingle, func(req *nebula.Request, updateTime int) {
		var openId string
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			return
		}
		singles, err := controller.Center.GetAllSingle(openId, userId, GetUpdateTime(req, updateTime))
		if err != nil {
			req.ResponseErr(err)
			return
		}
		req.SetCompress(true)
		req.Response(singles)
	})

	_ = namespace.On(InfoFinishSingle, func(req *nebula.Request, singleKey string, time int, score int, star int, pass bool) {
		var openId string
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		updateData := make(map[string]interface{})
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			req.ResponseErr(err)
			return
		}
		current := &player_single.SingleResult{
			Star:  star,
			Pass:  pass,
			Score: score,
			Time:  time,
			Rank:  0,
		}
		before, err := controller.Center.FinishSingle(&user, singleKey, user.SingleScore, current, updateData)
		if err != nil {
			return
		}
		message, err := controller.SendSingleMessage(req, userId, singleKey, reward.RewardFromGoldReward(&reward.GoldReward{Gold: utils.ToInt(singleKey) + star*10}))
		if err != nil {
			req.ResponseErr(err)
			return
		}
		if len(updateData) > 0 {
			if err := controller.Center.UpdateUser(&user, updateData); err != nil {
				req.ResponseErr(err)
				return
			}
		}
		language := GetLanguage(req)
		r := &model.GameSingleResult{
			Before:  before,
			Current: current,
			Result:  NewUserMessage(message, language, model.MessageTemplates),
		}
		req.Response(r)
	})

	_ = namespace.On(InfoStat, func(req *nebula.Request, key string, value int) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		_ = controller.Center.UpdateStatSingle(req, &user, controller.Center.StatData, key, int32(value), false)

		data := map[string]interface{}{}
		if err := controller.UpdateAchievements(req, &user, data); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		if err := controller.Center.UpdateUser(&user, data); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		req.Response(true)
	})

	_ = namespace.On(InfoFriend, func(req *nebula.Request, updateTime int) {
		// 获取好友信息
		var openId string
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		t := GetUpdateTime(req, updateTime)
		language := GetLanguage(req)
		var userList []model.User
		var gameUserList []*model.GameOtherUserExtend
		list, err := controller.Center.GetFriendList(openId, t, func(openIds []string) []UserBase {
			_ = controller.Center.FindUsers(openIds, "", nil, &userList)
			users := make([]UserBase, len(userList))
			gameUserList = make([]*model.GameOtherUserExtend, len(userList))
			var err error
			for index, u := range userList {
				if gameUserList[index], err = controller.GetUserPublicInfo(req, &u, language); err != nil {
					fmt.Println(err)
				}
				users[index] = &u
			}
			return users
		}, language, true)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		for index, u := range gameUserList {
			u.BindGameUser(list[index])
		}
		req.SetCompress(true)
		req.Response(list)
	})
	_ = namespace.On(InfoFriendInvited, func(req *nebula.Request, updateTime int) {
		// 获取好友信息
		var openId string
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		t := GetUpdateTime(req, updateTime)
		language := GetLanguage(req)
		var userList []model.User
		var gameUserList []*model.GameOtherUserExtend
		list, err := controller.Center.GetFriendList(openId, t, func(openIds []string) []UserBase {
			_ = controller.Center.FindUsers(openIds, "", nil, &userList)
			users := make([]UserBase, len(userList))
			gameUserList = make([]*model.GameOtherUserExtend, len(userList))
			for index, u := range userList {
				gameUserList[index], _ = controller.GetUserPublicInfo(req, &u, language)
				users[index] = &u
			}
			return users
		}, language, false)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		for index, u := range gameUserList {
			u.BindGameUser(list[index])
		}
		req.SetCompress(true)
		req.Response(list)
	})
	_ = namespace.On(InfoSearch, func(req *nebula.Request, keyword string) {
		var openId string
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		language := GetLanguage(req)
		var userList []model.User
		var gameUserList []*model.GameOtherUserExtend
		list, err := controller.Center.GetSearchList(openId, keyword, func(openIds []string) []UserBase {
			_ = controller.Center.FindUsers(openIds, keyword, nil, &userList)
			users := make([]UserBase, len(userList))
			gameUserList = make([]*model.GameOtherUserExtend, len(userList))
			for index, u := range userList {
				gameUserList[index], _ = controller.GetUserPublicInfo(req, &u, language)
				users[index] = &u
			}
			return users
		}, language)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		for index, u := range gameUserList {
			u.BindGameUser(list[index])
		}
		req.SetCompress(true)
		req.Response(list)
	})

	_ = namespace.On(InfoRank, func(req *nebula.Request, rankKey string, number int) {
		var openId string
		if err := req.Get("open_id", &openId); err != nil {
			return
		}
		language := GetLanguage(req)
		var userList []model.User
		var gameUserList []*model.GameOtherUserExtend
		list, err := controller.Center.GetRankList(openId, rankKey, number, func(openIds []string) []UserBase {
			_ = controller.Center.FindUsers(openIds, "", nil, &userList)
			users := make([]UserBase, len(userList))
			gameUserList = make([]*model.GameOtherUserExtend, len(userList))
			for index, u := range userList {
				gameUserList[index], _ = controller.GetUserPublicInfo(req, &u, language)
				users[index] = &u
			}
			return users
		}, language)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		for index, u := range gameUserList {
			u.BindRankUser(list[index])
		}
		req.SetCompress(true)
		req.Response(list)
	})
	_ = namespace.On(InfoRequestAd, func(req *nebula.Request, plane int) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			req.ResponseErr(err)
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			req.ResponseErr(err)
			return
		}
		t, err := NextAd(controller.Env, user.OpenId, AdPlane(plane))
		if err != nil {
			req.ResponseErr(err)
			return
		}
		reporter.Increase("ad.plane.request." + string(t.Plane))
		reporter.Increase("ad.brand.request." + t.Brand)
		//fmt.Println(t)
		req.Response(t)
	})
	_ = namespace.On(InfoResponseAd, func(req *nebula.Request, plane int) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		t, err := NextAd(controller.Env, user.OpenId, AdPlane(plane))
		if err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		if err := ResponseAd(controller.Env, user.OpenId, t); err != nil {
			req.ResponseErr(err)
			return
		}
		reporter.Increase("ad.plane.response." + string(t.Plane))
		reporter.Increase("ad.brand.response." + t.Brand)
		if err := controller.Center.UpdateStatSingle(req, &user, controller.Center.StatData, model.AdStat, 1, false); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		data := make(map[string]interface{})
		if err := controller.UpdateAchievementSingle(req, &user, model.AdAchievement, data); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		if err := controller.Center.UpdateUser(&user, data); err != nil {
			req.SendError(err)
			req.Response(false)
			return
		}
		req.Response(true)
	})
	_ = namespace.On(InfoReceiveReward, func(req *nebula.Request, messageId int) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		var user model.User
		if err := controller.Center.GetUser(userId, &user); err != nil {
			req.ResponseErr(err)
			return
		}
		reward, err := controller.ReceiveRewardMessage(req, &user, messageId)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		req.Response(reward)
	})
	_ = namespace.On(InfoGameResult, func(req *nebula.Request, userGameId int) {
		var userId int
		if err := req.Get("user_id", &userId); err != nil {
			return
		}
		//language := GetLanguage(req)
		result, err := controller.Center.GetUserGame(userId, userGameId)
		if err != nil {
			req.ResponseErr(err)
			return
		}
		Result := utils.ToType(result, reflect.TypeOf(&model.GameResult{})).(*model.GameResult)
		//Result.Reward.BindInfo(controller.Center.GetItem(language))
		req.Response(Result)
	})
}
